; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
; RUN: opt -passes=instcombine,verify -S < %s | FileCheck %s --check-prefixes=ALL,INSTCOMBINE

; Make sure GVN won't undo the transformation:
; RUN: opt -passes=instcombine,gvn -S < %s | FileCheck %s --check-prefixes=ALL,INSTCOMBINEGVN

declare ptr @get_ptr.i8()
declare ptr @get_ptr.i32()
declare void @foo.i8(ptr)
declare void @foo.i32(ptr)

define i32 @test_gep_and_bitcast(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_gep_and_bitcast(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED_IN:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES_PHI:%.*]] = load i32, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i8()
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1
  br label %exit

bb2:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb1 ], [ %ptr2, %bb2 ]
  %res.phi = phi i32 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i32 @test_gep_and_bitcast_arg(ptr %obj, i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_gep_and_bitcast_arg(
; ALL-NEXT:  entry:
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED_IN:%.*]] = getelementptr inbounds i8, ptr [[OBJ:%.*]], i64 16
; ALL-NEXT:    [[RES_PHI:%.*]] = load i32, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1
  br label %exit

bb2:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb1 ], [ %ptr2, %bb2 ]
  %res.phi = phi i32 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i32 @test_gep_and_bitcast_phi(i1 %cond, i1 %cond2, i1 %cond3) {
; ALL-LABEL: @test_gep_and_bitcast_phi(
; ALL-NEXT:  entry:
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    [[OBJ1:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br label [[MERGE:%.*]]
; ALL:       bb2:
; ALL-NEXT:    [[OBJ2_TYPED:%.*]] = call ptr @get_ptr.i32()
; ALL-NEXT:    br label [[MERGE]]
; ALL:       merge:
; ALL-NEXT:    [[OBJ:%.*]] = phi ptr [ [[OBJ1]], [[BB1]] ], [ [[OBJ2_TYPED]], [[BB2]] ]
; ALL-NEXT:    [[ANOTHER_PHI:%.*]] = phi ptr [ [[OBJ1]], [[BB1]] ], [ null, [[BB2]] ]
; ALL-NEXT:    call void @foo.i8(ptr [[ANOTHER_PHI]])
; ALL-NEXT:    br i1 [[COND2:%.*]], label [[BB3:%.*]], label [[BB4:%.*]]
; ALL:       bb3:
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb4:
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED_IN:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES_PHI:%.*]] = load i32, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND3:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  br i1 %cond, label %bb1, label %bb2

bb1:
  %obj1 = call ptr @get_ptr.i8()
  br label %merge

bb2:
  %obj2.typed = call ptr @get_ptr.i32()
  br label %merge

merge:
  %obj = phi ptr [ %obj1, %bb1 ], [ %obj2.typed, %bb2 ]
  %another_phi = phi ptr [ %obj1, %bb1 ], [ null, %bb2 ]
  call void @foo.i8(ptr %another_phi)
  br i1 %cond2, label %bb3, label %bb4

bb3:
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1
  br label %exit

bb4:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb3 ], [ %ptr2, %bb4 ]
  %res.phi = phi i32 [ %res1, %bb3 ], [ %res2, %bb4 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond3, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i32 @test_gep_i32ptr(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_gep_i32ptr(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i32()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED:%.*]] = getelementptr inbounds i32, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES_PHI:%.*]] = load i32, ptr [[PTR_TYPED]], align 4
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i32()
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1.typed = getelementptr inbounds i32, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1.typed
  br label %exit

bb2:
  %ptr2.typed = getelementptr inbounds i32, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2.typed
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1.typed, %bb1 ], [ %ptr2.typed, %bb2 ]
  %res.phi = phi i32 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i32 @test_gep_and_bitcast_gep_base_ptr(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_gep_and_bitcast_gep_base_ptr(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ0:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED_IN:%.*]] = getelementptr inbounds i8, ptr [[OBJ0]], i64 32
; ALL-NEXT:    [[RES_PHI:%.*]] = load i32, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  %obj0 = call ptr @get_ptr.i8()
  %obj = getelementptr inbounds i8, ptr %obj0, i64 16
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1
  br label %exit

bb2:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb1 ], [ %ptr2, %bb2 ]
  %res.phi = phi i32 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i32 @test_gep_and_bitcast_same_bb(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_gep_and_bitcast_same_bb(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[EXIT:%.*]], label [[BB2:%.*]]
; ALL:       bb2:
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED_IN:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES_PHI:%.*]] = load i32, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED_IN]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i8()
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1
  br i1 %cond, label %exit, label %bb2

bb2:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %entry ], [ %ptr2, %bb2 ]
  %res.phi = phi i32 [ %res1, %entry ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i32 @test_gep_and_bitcast_same_bb_and_extra_use(i1 %cond, i1 %cond2) {
; INSTCOMBINE-LABEL: @test_gep_and_bitcast_same_bb_and_extra_use(
; INSTCOMBINE-NEXT:  entry:
; INSTCOMBINE-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; INSTCOMBINE-NEXT:    [[PTR1:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; INSTCOMBINE-NEXT:    call void @foo.i32(ptr nonnull [[PTR1]])
; INSTCOMBINE-NEXT:    br i1 [[COND:%.*]], label [[EXIT:%.*]], label [[BB2:%.*]]
; INSTCOMBINE:       bb2:
; INSTCOMBINE-NEXT:    [[PTR2:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; INSTCOMBINE-NEXT:    br label [[EXIT]]
; INSTCOMBINE:       exit:
; INSTCOMBINE-NEXT:    [[PTR_TYPED:%.*]] = phi ptr [ [[PTR1]], [[ENTRY:%.*]] ], [ [[PTR2]], [[BB2]] ]
; INSTCOMBINE-NEXT:    [[RES_PHI:%.*]] = load i32, ptr [[PTR_TYPED]], align 4
; INSTCOMBINE-NEXT:    store i32 1, ptr [[PTR_TYPED]], align 4
; INSTCOMBINE-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; INSTCOMBINE-NEXT:    ret i32 [[RES]]
;
; INSTCOMBINEGVN-LABEL: @test_gep_and_bitcast_same_bb_and_extra_use(
; INSTCOMBINEGVN-NEXT:  entry:
; INSTCOMBINEGVN-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; INSTCOMBINEGVN-NEXT:    [[PTR1:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; INSTCOMBINEGVN-NEXT:    call void @foo.i32(ptr nonnull [[PTR1]])
; INSTCOMBINEGVN-NEXT:    br i1 [[COND:%.*]], label [[EXIT:%.*]], label [[BB2:%.*]]
; INSTCOMBINEGVN:       bb2:
; INSTCOMBINEGVN-NEXT:    br label [[EXIT]]
; INSTCOMBINEGVN:       exit:
; INSTCOMBINEGVN-NEXT:    [[RES_PHI:%.*]] = load i32, ptr [[PTR1]], align 4
; INSTCOMBINEGVN-NEXT:    store i32 1, ptr [[PTR1]], align 4
; INSTCOMBINEGVN-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; INSTCOMBINEGVN-NEXT:    ret i32 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i8()
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  call void @foo.i32(ptr %ptr1)
  %res1 = load i32, ptr %ptr1
  br i1 %cond, label %exit, label %bb2

bb2:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %entry ], [ %ptr2, %bb2 ]
  %res.phi = phi i32 [ %res1, %entry ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i8 @test_gep(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_gep(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES_PHI:%.*]] = load i8, ptr [[PTR_TYPED]], align 1
; ALL-NEXT:    store i8 1, ptr [[PTR_TYPED]], align 1
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i8 [[RES_PHI]], i8 1
; ALL-NEXT:    ret i8 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i8()
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i8, ptr %ptr1
  br label %exit

bb2:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i8, ptr %ptr2
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb1 ], [ %ptr2, %bb2 ]
  %res.phi = phi i8 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i8 1, ptr %ptr.typed
  %res.load = load i8, ptr %ptr.typed
  %res = select i1 %cond2, i8 %res.phi, i8 %res.load
  ret i8 %res
}

define i32 @test_extra_uses(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_extra_uses(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    [[PTR1:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES1:%.*]] = load i32, ptr [[PTR1]], align 4
; ALL-NEXT:    call void @foo.i32(ptr nonnull [[PTR1]])
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    [[PTR2:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES2:%.*]] = load i32, ptr [[PTR2]], align 4
; ALL-NEXT:    call void @foo.i32(ptr nonnull [[PTR2]])
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED:%.*]] = phi ptr [ [[PTR1]], [[BB1]] ], [ [[PTR2]], [[BB2]] ]
; ALL-NEXT:    [[RES_PHI:%.*]] = phi i32 [ [[RES1]], [[BB1]] ], [ [[RES2]], [[BB2]] ]
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i8()
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1
  call void @foo.i32(ptr %ptr1)
  br label %exit

bb2:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2
  call void @foo.i32(ptr %ptr2)
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb1 ], [ %ptr2, %bb2 ]
  %res.phi = phi i32 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i32 @test_extra_uses_non_inbounds(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_extra_uses_non_inbounds(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    [[PTR1:%.*]] = getelementptr i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES1:%.*]] = load i32, ptr [[PTR1]], align 4
; ALL-NEXT:    call void @foo.i32(ptr nonnull [[PTR1]])
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    [[PTR2:%.*]] = getelementptr i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES2:%.*]] = load i32, ptr [[PTR2]], align 4
; ALL-NEXT:    call void @foo.i32(ptr nonnull [[PTR2]])
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED:%.*]] = phi ptr [ [[PTR1]], [[BB1]] ], [ [[PTR2]], [[BB2]] ]
; ALL-NEXT:    [[RES_PHI:%.*]] = phi i32 [ [[RES1]], [[BB1]] ], [ [[RES2]], [[BB2]] ]
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i8()
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1 = getelementptr i8, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1
  call void @foo.i32(ptr %ptr1)
  br label %exit

bb2:
  %ptr2 = getelementptr i8, ptr %obj, i64 16
  %res2 = load i32, ptr %ptr2
  call void @foo.i32(ptr %ptr2)
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb1 ], [ %ptr2, %bb2 ]
  %res.phi = phi i32 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i32 @test_extra_uses_multiple_geps(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_extra_uses_multiple_geps(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    [[PTR1:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES1:%.*]] = load i32, ptr [[PTR1]], align 4
; ALL-NEXT:    call void @foo.i32(ptr nonnull [[PTR1]])
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    [[PTR2_1:%.*]] = getelementptr i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES2:%.*]] = load i32, ptr [[PTR2_1]], align 4
; ALL-NEXT:    call void @foo.i32(ptr nonnull [[PTR2_1]])
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED:%.*]] = phi ptr [ [[PTR1]], [[BB1]] ], [ [[PTR2_1]], [[BB2]] ]
; ALL-NEXT:    [[RES_PHI:%.*]] = phi i32 [ [[RES1]], [[BB1]] ], [ [[RES2]], [[BB2]] ]
; ALL-NEXT:    store i32 1, ptr [[PTR_TYPED]], align 4
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i32 [[RES_PHI]], i32 1
; ALL-NEXT:    ret i32 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i8()
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i32, ptr %ptr1
  call void @foo.i32(ptr %ptr1)
  br label %exit

bb2:
  %ptr2.0 = getelementptr i8, ptr %obj, i64 8
  %ptr2.1 = getelementptr inbounds i8, ptr %ptr2.0, i64 8
  %res2 = load i32, ptr %ptr2.1
  call void @foo.i32(ptr %ptr2.1)
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb1 ], [ %ptr2.1, %bb2 ]
  %res.phi = phi i32 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i32 1, ptr %ptr.typed
  %res.load = load i32, ptr %ptr.typed
  %res = select i1 %cond2, i32 %res.phi, i32 %res.load
  ret i32 %res
}

define i8 @test_gep_extra_uses(i1 %cond, i1 %cond2) {
; ALL-LABEL: @test_gep_extra_uses(
; ALL-NEXT:  entry:
; ALL-NEXT:    [[OBJ:%.*]] = call ptr @get_ptr.i8()
; ALL-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; ALL:       bb1:
; ALL-NEXT:    [[PTR1:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES1:%.*]] = load i8, ptr [[PTR1]], align 1
; ALL-NEXT:    call void @foo.i8(ptr nonnull [[PTR1]])
; ALL-NEXT:    br label [[EXIT:%.*]]
; ALL:       bb2:
; ALL-NEXT:    [[PTR2:%.*]] = getelementptr inbounds i8, ptr [[OBJ]], i64 16
; ALL-NEXT:    [[RES2:%.*]] = load i8, ptr [[PTR2]], align 1
; ALL-NEXT:    call void @foo.i8(ptr nonnull [[PTR2]])
; ALL-NEXT:    br label [[EXIT]]
; ALL:       exit:
; ALL-NEXT:    [[PTR_TYPED:%.*]] = phi ptr [ [[PTR1]], [[BB1]] ], [ [[PTR2]], [[BB2]] ]
; ALL-NEXT:    [[RES_PHI:%.*]] = phi i8 [ [[RES1]], [[BB1]] ], [ [[RES2]], [[BB2]] ]
; ALL-NEXT:    store i8 1, ptr [[PTR_TYPED]], align 1
; ALL-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], i8 [[RES_PHI]], i8 1
; ALL-NEXT:    ret i8 [[RES]]
;
entry:
  %obj = call ptr @get_ptr.i8()
  br i1 %cond, label %bb1, label %bb2

bb1:
  %ptr1 = getelementptr inbounds i8, ptr %obj, i64 16
  %res1 = load i8, ptr %ptr1
  call void @foo.i8(ptr %ptr1)
  br label %exit

bb2:
  %ptr2 = getelementptr inbounds i8, ptr %obj, i64 16
  %res2 = load i8, ptr %ptr2
  call void @foo.i8(ptr %ptr2)
  br label %exit

exit:
  %ptr.typed = phi ptr [ %ptr1, %bb1 ], [ %ptr2, %bb2 ]
  %res.phi = phi i8 [ %res1, %bb1 ], [ %res2, %bb2 ]
  store i8 1, ptr %ptr.typed
  %res.load = load i8, ptr %ptr.typed
  %res = select i1 %cond2, i8 %res.phi, i8 %res.load
  ret i8 %res
}

; `swifterror` addresses are restricted to load and stores and call arguments.
declare void @takeAddress(ptr swifterror)

define ptr @test_dont_optimize_swifterror(i1 %cond, i1 %cond2, ptr %ptr) {
; INSTCOMBINE-LABEL: @test_dont_optimize_swifterror(
; INSTCOMBINE-NEXT:  entry:
; INSTCOMBINE-NEXT:    [[OBJ:%.*]] = alloca swifterror ptr, align 8
; INSTCOMBINE-NEXT:    [[OBJ2:%.*]] = alloca swifterror ptr, align 8
; INSTCOMBINE-NEXT:    call void @takeAddress(ptr nonnull swifterror [[OBJ]])
; INSTCOMBINE-NEXT:    call void @takeAddress(ptr nonnull swifterror [[OBJ2]])
; INSTCOMBINE-NEXT:    store ptr [[PTR:%.*]], ptr [[OBJ]], align 8
; INSTCOMBINE-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; INSTCOMBINE:       bb1:
; INSTCOMBINE-NEXT:    [[RES1:%.*]] = load ptr, ptr [[OBJ]], align 8
; INSTCOMBINE-NEXT:    br label [[EXIT:%.*]]
; INSTCOMBINE:       bb2:
; INSTCOMBINE-NEXT:    [[RES2:%.*]] = load ptr, ptr [[OBJ2]], align 8
; INSTCOMBINE-NEXT:    br label [[EXIT]]
; INSTCOMBINE:       exit:
; INSTCOMBINE-NEXT:    [[RES_PHI:%.*]] = phi ptr [ [[RES1]], [[BB1]] ], [ [[RES2]], [[BB2]] ]
; INSTCOMBINE-NEXT:    store ptr null, ptr [[OBJ]], align 8
; INSTCOMBINE-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], ptr [[RES_PHI]], ptr null
; INSTCOMBINE-NEXT:    ret ptr [[RES]]
;
; INSTCOMBINEGVN-LABEL: @test_dont_optimize_swifterror(
; INSTCOMBINEGVN-NEXT:  entry:
; INSTCOMBINEGVN-NEXT:    [[OBJ:%.*]] = alloca swifterror ptr, align 8
; INSTCOMBINEGVN-NEXT:    [[OBJ2:%.*]] = alloca swifterror ptr, align 8
; INSTCOMBINEGVN-NEXT:    call void @takeAddress(ptr nonnull swifterror [[OBJ]])
; INSTCOMBINEGVN-NEXT:    call void @takeAddress(ptr nonnull swifterror [[OBJ2]])
; INSTCOMBINEGVN-NEXT:    store ptr [[PTR:%.*]], ptr [[OBJ]], align 8
; INSTCOMBINEGVN-NEXT:    br i1 [[COND:%.*]], label [[BB1:%.*]], label [[BB2:%.*]]
; INSTCOMBINEGVN:       bb1:
; INSTCOMBINEGVN-NEXT:    br label [[EXIT:%.*]]
; INSTCOMBINEGVN:       bb2:
; INSTCOMBINEGVN-NEXT:    [[RES2:%.*]] = load ptr, ptr [[OBJ2]], align 8
; INSTCOMBINEGVN-NEXT:    br label [[EXIT]]
; INSTCOMBINEGVN:       exit:
; INSTCOMBINEGVN-NEXT:    [[RES_PHI:%.*]] = phi ptr [ [[PTR]], [[BB1]] ], [ [[RES2]], [[BB2]] ]
; INSTCOMBINEGVN-NEXT:    store ptr null, ptr [[OBJ]], align 8
; INSTCOMBINEGVN-NEXT:    [[RES:%.*]] = select i1 [[COND2:%.*]], ptr [[RES_PHI]], ptr null
; INSTCOMBINEGVN-NEXT:    ret ptr [[RES]]
;
entry:
  %obj = alloca swifterror ptr, align 8
  %obj2 = alloca swifterror ptr, align 8
  call void @takeAddress(ptr swifterror %obj)
  call void @takeAddress(ptr swifterror %obj2)
  store ptr %ptr, ptr %obj, align 8
  br i1 %cond, label %bb1, label %bb2

bb1:                                              ; preds = %entry
  %res1 = load ptr, ptr %obj, align 8
  br label %exit

bb2:                                              ; preds = %entry
  %res2 = load ptr, ptr %obj2, align 8
  br label %exit

exit:                                             ; preds = %bb2, %bb1
  %res.phi = phi ptr [ %res1, %bb1 ], [ %res2, %bb2 ]
  store ptr null, ptr %obj, align 8
  %res = select i1 %cond2, ptr %res.phi, ptr null
  ret ptr %res
}
